package com.linkgie.galaxyframework.crypto;

import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.linkgie.galaxyframework.crypto.context.AsymmetricCipherServiceImpl;
import com.linkgie.galaxyframework.crypto.context.AsymmetricCipherSignatureServiceImpl;
import com.linkgie.galaxyframework.crypto.context.AsymmetricStreamingCipherServiceImpl;
import com.linkgie.galaxyframework.crypto.context.CryptoServiceBase;
import com.linkgie.galaxyframework.crypto.context.CryptogramEncoding;
import com.linkgie.galaxyframework.crypto.context.Encoding;
import com.linkgie.galaxyframework.crypto.context.HashServiceImpl;
import com.linkgie.galaxyframework.crypto.context.RandomServiceImpl;
import com.linkgie.galaxyframework.crypto.context.SignatureServiceImpl;
import com.linkgie.galaxyframework.crypto.context.SymmetricCipherServiceImpl;
import com.linkgie.galaxyframework.crypto.spi.AsymmetricCipherFuncSPI;
import com.linkgie.galaxyframework.crypto.spi.AsymmetricFuncSPI;
import com.linkgie.galaxyframework.crypto.spi.AsymmetricStreamingCipherFuncSPI;
import com.linkgie.galaxyframework.crypto.spi.CryptoFuncSPI;
import com.linkgie.galaxyframework.crypto.spi.CryptoServiceProvider;
import com.linkgie.galaxyframework.crypto.spi.Cryptobytes;
import com.linkgie.galaxyframework.crypto.spi.CryptobytesCodec;
import com.linkgie.galaxyframework.crypto.spi.HashFuncSPI;
import com.linkgie.galaxyframework.crypto.spi.Hashbytes;
import com.linkgie.galaxyframework.crypto.spi.PrivateKeybytes;
import com.linkgie.galaxyframework.crypto.spi.PublicKeybytes;
import com.linkgie.galaxyframework.crypto.spi.RandomFuncSPI;
import com.linkgie.galaxyframework.crypto.spi.SignatureFuncSPI;
import com.linkgie.galaxyframework.crypto.spi.Signaturebytes;
import com.linkgie.galaxyframework.crypto.spi.SymmetricFuncSPI;
import com.linkgie.galaxyframework.crypto.spi.SymmetricKeybytes;
import com.linkgie.galaxyframework.utils.Base58Encoding;
import com.linkgie.galaxyframework.utils.ExecutionContext;
import com.linkgie.galaxyframework.utils.ExecutionContextHandle;
import com.linkgie.galaxyframework.utils.ExecutionScheduler;
import com.linkgie.galaxyframework.utils.RethrowException;
import com.linkgie.galaxyframework.utils.service.ServiceContext;
import com.linkgie.galaxyframework.utils.service.ServiceProvider;

/**
 * {@link Crypto} 是密码算法库的门面；
 * 
 * @author liso
 *
 */
public final class Crypto {

	private static final String HASH_SM3 = "SM3";

	private static final String HASH_RIPEMD1602 = "RIPEMD160";

	private static final Logger LOGGER = LoggerFactory.getLogger(Crypto.class);

	/**
	 * 保存状态的上下文空间；
	 */
	private static class CryptoContext {

		@SuppressWarnings("unused")
		private ServiceContext<CryptoServiceProvider> providerManager;

		private final AlgorithmMap algorithmMap = new AlgorithmMap();

		private final CryptoCodec cryptoCodec = new GlobalDecoder(algorithmMap, this);

		private final Map<String, CryptoServiceBase<?>> cryptoServices = new ConcurrentHashMap<>();

		private PublicKeyAddressGenerator standardAddressGenerator;

		/**
		 * 密码学哈希算法服务；
		 * <p>
		 * 
		 * 如果不存在指定算法的实现，则返回 null；
		 * 
		 * @param algorithm
		 * @return
		 */
		private HashService retrieveHashService(String algorithm) {
			CryptoService cryptoService = this.retrieveCryptoService(algorithm);
			if (cryptoService == null) {
				return null;
			}
			if (!(cryptoService instanceof HashService)) {
				return null;
			}
			return (HashService) cryptoService;
		}

		/**
		 * 返回指定的密码算法服务；
		 * <p>
		 * 
		 * 如果未注册该算法，则返回 null；
		 * 
		 * @param algorithm
		 * @return
		 */
		private CryptoService retrieveCryptoService(String algorithm) {
			return cryptoServices.get(algorithm);
		}

	}

	private static final CryptoContext DEFAULT_CONTEXT;

	private static final ThreadLocal<CryptoContext> currentContext = new ThreadLocal<>();

	static {
		ClassLoader defaultClassLoader = getDefaultClassLoader();
		DEFAULT_CONTEXT = initialCryptoContext(defaultClassLoader);
	}

	private static CryptoContext context() {
		CryptoContext cryptoContext = currentContext.get();
		return cryptoContext == null ? DEFAULT_CONTEXT : cryptoContext;
	}

	private static ClassLoader getDefaultClassLoader() {
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		return classLoader;
	}

	private static CryptoContext initialCryptoContext(ClassLoader classLoader) {
		// 装载当前的类加载器下所有的密码服务提供者；
		CryptoContext context = new CryptoContext();

		ServiceContext<CryptoServiceProvider> providerManager = ServiceContext.load(CryptoServiceProvider.class,
				classLoader);
		if (providerManager.getSize() == 0) {
			providerManager.loadFrom(Crypto.class.getClassLoader());
		}
		context.providerManager = providerManager;

		// 注册密码服务；
		Collection<ServiceProvider<CryptoServiceProvider>> providers = providerManager.getProviders();

		for (ServiceProvider<CryptoServiceProvider> provider : providers) {
			registerCryptoProvider(provider.getService(), context);
		}

		initUUIDGenerator(context);
		return context;
	}

	/**
	 * 注册密码服务；
	 * 
	 * @param cryptoProvider
	 * @param cryptogramBuilder
	 */
	private static void registerCryptoProvider(CryptoServiceProvider cryptoProvider, CryptoContext context) {

		Collection<CryptoFuncSPI> spiList = cryptoProvider.getServices();
		for (CryptoFuncSPI cryptoFuncSPI : spiList) {
			Algorithm algorithm = cryptoFuncSPI.getAlgorithm();

			Algorithm conflictedAlgorithm = context.algorithmMap.getAlgorithm(algorithm.getCode());
			if (conflictedAlgorithm != null) {
				LOGGER.error(
						"The algorithm code has been registered as [{}:{}]!  The newly registerd one[{}:{}] will be igored!",
						conflictedAlgorithm.getCode(), conflictedAlgorithm.getName(), algorithm.getCode(),
						algorithm.getName());
				continue;
			}
			conflictedAlgorithm = context.algorithmMap.getAlgorithm(algorithm.getName());
			if (conflictedAlgorithm != null) {
				LOGGER.error(
						"The algorithm name has been registered as [{}:{}]!  The newly registerd one[{}:{}] will be igored!",
						conflictedAlgorithm.getCode(), conflictedAlgorithm.getName(), algorithm.getCode(),
						algorithm.getName());
				continue;
			}

			CryptoServiceBase<?> conflictedService = context.cryptoServices.get(algorithm.getName());
			if (conflictedService != null) {
				LOGGER.error(
						"Crypto algorithm name conflict! -- Algorithm [{}] is already registered by [{}], cann't be registered by another[{}]!",
						algorithm, conflictedService.getProvidedService().getClass().getName(),
						cryptoFuncSPI.getClass().getName());
				continue;
			}

			regiserCryptoService(cryptoFuncSPI, context);
		}
	}

	private static void initUUIDGenerator(CryptoContext context) {
		// 创建标准的 UUID 生成器；
		// 如果缺少 SM3 和 RIPEMD160 算法实现，则不创建国密 UUID 生成器；
		HashService ripemd160 = context.retrieveHashService(HASH_RIPEMD1602);
		if (ripemd160 == null) {
			LOGGER.warn("Cann't initialized the UUID generator because RIPEMD160 hash algorithm is not found!");
			return;
		}

		// 创建国密标准的地址生成器；
		HashService sm3 = context.retrieveHashService(HASH_SM3);
		if (sm3 == null) {
			LOGGER.warn("Cann't initialized the standard UUID generator because SM3 hash algorithm is not found!");
		} else {
			// 如果缺少 SM3 和 RIPEMD160 算法实现，则不创建国密 UUID 生成器；
			context.standardAddressGenerator = BlockchainAddressGenerator.createStandardGenerator(sm3, ripemd160);
		}

		// HashService sha256 = retrieveHashService("SHA256");
		// // 如果缺少 SHA256 和 RIPEMD160 算法实现，则不创建标准 UUID 生成器；
		// if (sha256 == null) {
		// LOGGER.warn("Cann't initialized the standard UUID generator because SHA256
		// hash algorithm is not found!");
		// } else {
		// standardAddressGenerator = new BlockchainAddressGenerator(sha256, ripemd160,
		// STANDARD_ADDRESS_ENCODING_PREFIX, sha256);
		// }
	}

	private static void regiserCryptoService(CryptoFuncSPI cryptoFuncSPI, CryptoContext context) {
		Algorithm algorithm = cryptoFuncSPI.getAlgorithm();

		CryptoServiceBase<?> cryptoService = null;
		switch (algorithm.getType()) {
			case HASH:
				if (!(cryptoFuncSPI instanceof HashFuncSPI)) {
					// 声明的算法和实现的密码服务接口不匹配；
					LOGGER.error(
							"Ignore the crypto service that declare Hash-Algorithm but does not implement the hash function SPI! --{}",
							cryptoFuncSPI.getClass().getName());
					return;
				}
				cryptoService = new HashServiceImpl((HashFuncSPI) cryptoFuncSPI);
				break;
			case RANDOM:
				if (!(cryptoFuncSPI instanceof RandomFuncSPI)) {
					// 声明的算法和实现的密码服务接口不匹配；
					LOGGER.error(
							"Ignore the crypto service that declare Random-Algorithm but does not implement the random function SPI! --{}",
							cryptoFuncSPI.getClass().getName());
					return;
				}
				cryptoService = new RandomServiceImpl((RandomFuncSPI) cryptoFuncSPI);
				break;
			case SYMMETRIC:
				if (!(cryptoFuncSPI instanceof SymmetricFuncSPI)) {
					// 声明的算法和实现的密码服务接口不匹配；
					LOGGER.error(
							"Ignore the crypto service that declare Symmetric-Algorithm but does not implement the symmetirc function SPI! --{}",
							cryptoFuncSPI.getClass().getName());
					return;
				}
				cryptoService = new SymmetricCipherServiceImpl((SymmetricFuncSPI) cryptoFuncSPI);
				break;
			case ASYMMETRIC:
				if (cryptoFuncSPI instanceof SignatureFuncSPI && cryptoFuncSPI instanceof AsymmetricCipherFuncSPI) {
					if (cryptoFuncSPI instanceof AsymmetricStreamingCipherFuncSPI) {
						cryptoService = new AsymmetricStreamingCipherServiceImpl(
								(AsymmetricStreamingCipherFuncSPI) cryptoFuncSPI);
					} else {
						cryptoService = new AsymmetricCipherSignatureServiceImpl((AsymmetricFuncSPI) cryptoFuncSPI);
					}
				} else if (cryptoFuncSPI instanceof SignatureFuncSPI) {
					cryptoService = new SignatureServiceImpl((SignatureFuncSPI) cryptoFuncSPI);
				} else if (cryptoFuncSPI instanceof AsymmetricCipherFuncSPI) {
					if (cryptoFuncSPI instanceof AsymmetricStreamingCipherFuncSPI) {
						cryptoService = new AsymmetricStreamingCipherServiceImpl(
								(AsymmetricStreamingCipherFuncSPI) cryptoFuncSPI);
					} else {
						cryptoService = new AsymmetricCipherServiceImpl((AsymmetricCipherFuncSPI) cryptoFuncSPI);
					}
				} else {
					// 声明的算法和实现的密码服务接口不匹配；
					LOGGER.error(
							"Ignore the crypto service that declare Asymmetric-Algorithm but does not implement any aymmetric function SPI! --{}",
							cryptoFuncSPI.getClass().getName());
					return;
				}
				break;
			default:
				throw new IllegalStateException("Unknown cryptography type! --" + algorithm.getType());
		}

		context.cryptoServices.put(algorithm.getName(), cryptoService);
		context.algorithmMap.add(algorithm);
	}

	/**
	 * 切换至指定的上下文执行；
	 * 
	 * <p>
	 * 在指定的 runnable 执行过程中，对 Crypto 的调用都是基于指定的上下文加载的密码服务；
	 * <p>
	 * 注意：当在 runnable 执行中存在多线程调用的情形，如果要在不同的线程中都要保持相同的上下文，则需要开发者自行将上下文传递至其它线程执行；
	 * 
	 * @param contextClassLoader
	 * @param runnable
	 */
	public static void switchContext(ClassLoader contextClassLoader, Runnable runnable) {
		ExecutionContext context = createCryptoContext(contextClassLoader);
		context.run(runnable);
	}

	/**
	 * 切换至指定的上下文执行；
	 * 
	 * <p>
	 * 在指定的 callable 执行过程中，对 Crypto 的调用都是基于指定的上下文加载的密码服务；
	 * <p>
	 * 注意：当在 callable 执行中存在多线程调用的情形，如果要在不同的线程中都要保持相同的上下文，则需要开发者自行将上下文传递至其它线程执行；
	 * 
	 * @param <T>
	 * @param contextClassLoader
	 * @param callable
	 * @return
	 * @throws RethrowException
	 */
	public static <T> T switchContext(ClassLoader contextClassLoader, Callable<T> callable) {
		ExecutionContext context = createCryptoContext(contextClassLoader);
		return context.call(callable);
	}

	public static ExecutionContext createCryptoContext(ClassLoader contextClassLoader){
		CryptoContext context = initialCryptoContext(contextClassLoader);
		return ExecutionScheduler.buildContext(new ExecutionContextHandle<CryptoContext>() {

			@Override
			public CryptoContext init() {
				Crypto.currentContext.set(context);
				return null;
			}

			@Override
			public void clean(CryptoContext context) {
				Crypto.currentContext.remove();
			}
			
		});
	}

	/**
	 * 支持的密码算法列表；
	 * 
	 * @return
	 */
	public static Set<String> getAlgorithmNames() {
		return Collections.unmodifiableSet(context().cryptoServices.keySet());
	}

	/**
	 * 支持的密码算法列表；
	 * 
	 * @return
	 */
	public static Collection<Algorithm> getAlgorithms() {
		return Collections.unmodifiableCollection(context().algorithmMap.getAlgorithms());
	}

	/**
	 * 返回指定名称的密码算法定义；
	 * 
	 * @return
	 */
	public static Algorithm getAlgorithm(String name) {
		return context().algorithmMap.getAlgorithm(name);
	}

	public static CryptoCodec getCodec() {
		return context().cryptoCodec;
	}

	/**
	 * 基于 SM3 和 RIPEMD160 算法的国密标准地址生成器；
	 * <p>
	 * 
	 * 此实现的 UUID 生成算法与比特币地址的生成算法一致，只是用国密 SM3 算法替换 SHA256 算法， 同时, Base58Check
	 * 编码前缀值采用与比特币地址的前缀值 0x00， 但扩展为 32 位，以便作为全局ID使用时可表达更多类型；
	 * <p>
	 * 
	 * 如果当前应用代码没有加载注册 SM3 或 RIPEMD160 算法，则返回 null；
	 * 
	 * @return
	 */
	public static PublicKeyAddressGenerator getStandardPublicKeyAddressGenerator() {
		PublicKeyAddressGenerator generator = context().standardAddressGenerator;
		if (generator == null) {
			throw new CryptoException(
					"The StandardPublicKeyAddressGenerator is not initialized because of no hash algorithm [" + HASH_SM3
							+ "] or [" + HASH_RIPEMD1602 + "] provider!");
		}
		return generator;
	}

	/**
	 * 返回基于 SM3 和 RIPEMD160 的地址生成算法；
	 * 
	 * <p>
	 * 此方法采用固定的种子 0 ；
	 * 
	 * @return
	 */
	public static BinaryAddressGenerator getStandardBinaryAddressGenerator() {
		return getBinaryAddressGenerator(HASH_SM3, HASH_RIPEMD1602);
	}

	/**
	 * 返回基于 SM3 和 RIPEMD160 的地址生成算法；
	 * 
	 * @return
	 */
	public static BinaryAddressGenerator getStandardBinaryAddressGenerator(long seed) {
		return getBinaryAddressGenerator(seed, HASH_SM3, HASH_RIPEMD1602);
	}

	public static BinaryAddressGenerator getBinaryAddressGenerator(String contentHashAlgorithm) {
		return getBinaryAddressGenerator(contentHashAlgorithm, HASH_RIPEMD1602);
	}

	/**
	 * 返回基于指定算法的地址生成算法；
	 * 
	 * <p>
	 * 此方法采用固定的种子 0 ；
	 * 
	 * @param contentHashAlgorithm 内容哈希算法；用于执行一次哈希；
	 * @param addressHashAlgorithm 地址哈希算法；用于执行二次哈希；
	 * @return
	 */
	public static BinaryAddressGenerator getBinaryAddressGenerator(String contentHashAlgorithm,
			String addressHashAlgorithm) {
		return getBinaryAddressGenerator(0, contentHashAlgorithm, addressHashAlgorithm);
	}

	public static BinaryAddressGenerator getBinaryAddressGenerator(long seed, String contentHashAlgorithm,
			String addressHashAlgorithm) {
		HashService contentHasher = getHashService(contentHashAlgorithm);
		HashService addressHasher = getHashService(addressHashAlgorithm);
		return new DoubleHashAddressGenerator(seed, contentHasher, addressHasher);
	}

	// /**
	// * 创建基于 SHA256 和 RIPEMD160 算法的标准 UUID 生成器；
	// * <p>
	// *
	// * 注：此实现的 UUID 生成算法与比特币地址的生成算法一致；
	// *
	// * @param prefix UUID 的 Base58Check 编码前缀，用于识别 UUID 的类型；
	// * @return
	// */
	// public static PublicKeyAddressGenerator createStandardUUIDGenerator(int
	// prefix) {
	// // 创建标准的 UUID 生成器；
	// HashFunc ripemd160 = getHashService("RIPEMD160");
	// HashFunc sha256 = getHashService("SHA256");
	// return new BlockchainAddressGenerator(sha256, ripemd160, prefix, sha256);
	// }
	//
	// /**
	// * 创建基于 SM3 和 RIPEMD160 算法的国密标准 UUID 生成器；
	// * <p>
	// *
	// * @param prefix UUID 的 Base58Check 编码前缀，用于识别 UUID 的类型；
	// * @return
	// */
	// public static PublicKeyAddressGenerator createSMStandardUUIDGenerator(int
	// prefix) {
	// // 创建标准的 UUID 生成器；
	// HashFunc ripemd160 = getHashService("RIPEMD160");
	// HashFunc sm3 = getHashService("SM3");
	// return new BlockchainAddressGenerator(sm3, ripemd160, prefix, sm3);
	// }

	// public static CryptoAddressGenerator getCryptoAddressGenerator(String
	// hashAlgorithm, String addressAlgorithm) {
	//
	// }

	/**
	 * 解码指定的密码字节为指定的密码对象；
	 * <p>
	 * 
	 * 如果无法识别算法类型，或者无法识别密码数据类型，则返回 null；
	 * 
	 * @param <T>         密码对象类型；必须是 {@link Cryptogram} 的子类型
	 *                    {@link HashCode}、{@link PublicKey}、{@link PrivateKey}、{@link Signature}、{@link SymmetricKey}
	 *                    之一；
	 * @param cryptoBytes 密码数据
	 * @param type        密码数据类型；
	 * @return
	 */
	public static <T extends Cryptogram> T decode(byte[] cryptoBytes, Class<T> type) {
		return getCodec().decode(cryptoBytes, type);
	}

	/**
	 * 解码指定的密码字节为指定的密码对象；
	 * <p>
	 * 
	 * 如果无法识别算法类型，或者无法识别密码数据类型，则返回 null；
	 * 
	 * @param <T>        密码对象类型；必须是 {@link Cryptogram} 的子类型
	 *                   {@link HashCode}、{@link PublicKey}、{@link PrivateKey}、{@link Signature}、{@link SymmetricKey}
	 *                   之一；
	 * @param cryptoText 密码数据
	 * @param type       密码数据类型；
	 * @return
	 */
	public static <T extends Cryptogram> T decode(String cryptoText, Class<T> type) {
		return getCodec().decode(cryptoText, type);
	}

	public static RandomService getRandomService(String algorithm) {
		CryptoService cryptoService = getCryptoService(algorithm);
		if (!(cryptoService instanceof RandomService)) {
			throw new CryptoException("Algorithm[" + algorithm + "] is not a random algorithm!");
		}
		return (RandomService) cryptoService;
	}

	/**
	 * 返回指定的密码算法服务；
	 * <p>
	 * 
	 * 如果未注册该算法，则抛出 {@link CryptoException} 异常；
	 * 
	 * @param algorithm
	 * @return
	 */
	public static CryptoService getCryptoService(String algorithm) {
		return innerGetCryptoService(algorithm);
	}

	private static CryptoServiceBase<?> innerGetCryptoService(String algorithm) {
		CryptoServiceBase<?> cryptoService = context().cryptoServices.get(algorithm);
		if (cryptoService == null) {
			throw new CryptoException("No provider of crypto algorithm[" + algorithm + "]!");
		}
		return cryptoService;
	}

	/**
	 * 密码学哈希算法服务；
	 * <p>
	 * 
	 * 如果不存在指定算法的实现，则抛出异常 {@link CryptoException} ；
	 * 
	 * @param algorithm
	 * @return
	 */
	public static HashService getHashService(String algorithm) {
		CryptoService cryptoService = getCryptoService(algorithm);
		if (!(cryptoService instanceof HashService)) {
			throw new CryptoException("Algorithm[" + algorithm + "] is not a cryptographic hash algorithm!");
		}
		return (HashService) cryptoService;
	}

	/**
	 * 非对称密钥生成器；
	 * 
	 * @param algorithm
	 * @return
	 */
	public static AsymmetricKeyGenerator getAsymmetricKeyGenerator(String algorithm) {
		return getAsymmetricCryptoService(algorithm);
	}

	/**
	 * 非对称密码学算法服务；
	 * 
	 * @param algorithm
	 * @return
	 */
	public static AsymmetricCryptoService getAsymmetricCryptoService(String algorithm) {
		CryptoService cryptoService = getCryptoService(algorithm);
		if (!(cryptoService instanceof AsymmetricCryptoService)) {
			throw new CryptoException("Algorithm[" + algorithm + "] is not an asymmetric cryptosystem!");
		}
		return (AsymmetricCryptoService) cryptoService;
	}

	/**
	 * 签名算法服务；
	 * 
	 * @param algorithm
	 * @return
	 */
	public static SignatureService getSignatureService(String algorithm) {
		CryptoService cryptoService = getCryptoService(algorithm);
		if (!(cryptoService instanceof SignatureService)) {
			throw new CryptoException(
					"Algorithm[" + algorithm + "] is not an asymmetric cryptosystem supporting signature!");
		}
		return (SignatureService) cryptoService;
	}

	/**
	 * 非对称加解密算法服务；
	 * 
	 * @param algorithm
	 * @return
	 */
	public static AsymmetricCipherService getAsymmetricCipherService(String algorithm) {
		CryptoService cryptoService = getCryptoService(algorithm);
		if (!(cryptoService instanceof AsymmetricCipherService)) {
			throw new CryptoException(
					"Algorithm[" + algorithm + "] is not an asymmetric cryptosystem supporting encryption!");
		}
		return (AsymmetricCipherService) cryptoService;
	}

	/**
	 * 非对称流式加解密算法服务；
	 * <p>
	 * 
	 * 注：流式加解密算法支持对数据流执行加解密操作（{@link InputStream}, {@link OutputStream}）；
	 * 
	 * @param algorithm
	 * @return
	 */
	public static AsymmetricStreamingCipherService getAsymmetricStreamingCipherService(String algorithm) {
		CryptoService cryptoService = getCryptoService(algorithm);
		if (!(cryptoService instanceof AsymmetricStreamingCipherService)) {
			throw new CryptoException(
					"Algorithm[" + algorithm + "] is not an asymmetric cryptosystem supporting streaming encryption!");
		}
		return (AsymmetricStreamingCipherService) cryptoService;
	}

	/**
	 * 对称密钥生成器；
	 * 
	 * @param algorithm
	 * @return
	 */
	public static SymmetricKeyGenerator getSymmetricKeyGenerator(String algorithm) {
		return getSymmetricCipherService(algorithm);
	}

	/**
	 * 对称加解密算法服务；
	 * 
	 * @param algorithm
	 * @return
	 */
	public static SymmetricCipherService getSymmetricCipherService(String algorithm) {
		CryptoService cryptoService = getCryptoService(algorithm);
		if (!(cryptoService instanceof SymmetricCipherService)) {
			throw new CryptoException("Algorithm[" + algorithm + "] is not a symmetric cryptosystem!");
		}
		return (SymmetricCipherService) cryptoService;
	}

	private static class GlobalDecoder implements CryptoCodec {

		private final CryptoContext CONTEXT;

		private final AlgorithmMap ALGORITHMS;

		private final Encoding ENCODING;

		private final CryptogramEncoding CRYPTOGRAM_ENCODING;

		GlobalDecoder(AlgorithmMap algorithmMap, CryptoContext context) {
			this.ALGORITHMS = algorithmMap;
			this.ENCODING = Encoding.createInstance(algorithmMap);
			this.CRYPTOGRAM_ENCODING = new CryptogramEncoding(ENCODING);
			this.CONTEXT = context;
		}

		@Override
		public Algorithm decodeAlgorithm(byte[] cryptoBytes) {
			return ENCODING.decodeAlgorithm(cryptoBytes);
		}

		@Override
		public CryptogramType decodeType(byte[] cryptoBytes) {
			Algorithm algorithm = ENCODING.decodeAlgorithm(cryptoBytes);
			if (algorithm == null) {
				return null;
			}
			return decodeCryptogramType(algorithm, cryptoBytes);
		}

		@Override
		public boolean check(byte[] cryptoBytes, String algorithmName, CryptogramType type) {
			Algorithm algorithm = ENCODING.decodeAlgorithm(cryptoBytes);
			if (algorithm == null || algorithm.getName().equalsIgnoreCase(algorithmName)) {
				return false;
			}
			CryptogramType acturalType = decodeCryptogramType(algorithm, cryptoBytes);
			return acturalType == type;
		}

		private void checkAlgorithmEmpty() {
			if (ALGORITHMS.isEmpty()) {
				throw new CryptoException("No any crypto algorithm provider!");
			}
		}

		@Override
		public Cryptogram decode(byte[] cryptoBytes) {
			checkAlgorithmEmpty();

			Algorithm algorithm = ENCODING.decodeAlgorithm(cryptoBytes);
			if (algorithm == null) {
				return null;
			}
			Cryptobytes cbytes = decodeCryptobytes(algorithm, cryptoBytes);
			if (cbytes == null) {
				return null;
			}
			return CRYPTOGRAM_ENCODING.encode(algorithm, cbytes);
		}

		@SuppressWarnings("unchecked")
		@Override
		public <T extends Cryptogram> T decode(byte[] cryptoBytes, Class<T> type) {
			if (!isValidType(type)) {
				throw new IllegalArgumentException("Illegal type argument! --" + type.getName());
			}

			checkAlgorithmEmpty();

			Algorithm algorithm = ENCODING.decodeAlgorithm(cryptoBytes);
			if (algorithm == null) {
				throw new CryptoException(
						"No algorithm can't be identified for the specified " + type.getSimpleName() + " bytes!");
			}
			Cryptobytes cbytes = decodeCryptobytes(algorithm, cryptoBytes);

			switch (cbytes.getType()) {
				case HASH_DIGEST:
					if (type != HashCode.class) {
						throw new CryptoException("The specified type[" + type.getName()
								+ "] is not match the bytes of type[" + cbytes.getType() + "]!");
					}
					return (T) CRYPTOGRAM_ENCODING.encode(algorithm, (Hashbytes) cbytes);
				case SIGNATURE:
					if (type != Signature.class) {
						throw new CryptoException("The specified type[" + type.getName()
								+ "] is not match the bytes of type[" + cbytes.getType() + "]!");
					}
					return (T) CRYPTOGRAM_ENCODING.encode(algorithm, (Signaturebytes) cbytes);
				case PRIVATE_KEY:
					if (type != PrivateKey.class) {
						throw new CryptoException("The specified type[" + type.getName()
								+ "] is not match the bytes of type[" + cbytes.getType() + "]!");
					}
					return (T) CRYPTOGRAM_ENCODING.encode(algorithm, (PrivateKeybytes) cbytes);
				case PUBLIC_KEY:
					if (type != PublicKey.class) {
						throw new CryptoException("The specified type[" + type.getName()
								+ "] is not match the bytes of type[" + cbytes.getType() + "]!");
					}
					return (T) CRYPTOGRAM_ENCODING.encode(algorithm, (PublicKeybytes) cbytes);
				case SYMMETRIC_KEY:
					if (type != SymmetricKey.class) {
						throw new CryptoException("The specified type[" + type.getName()
								+ "] is not match the bytes of type[" + cbytes.getType() + "]!");
					}
					return (T) CRYPTOGRAM_ENCODING.encode(algorithm, (SymmetricKeybytes) cbytes);
				default:
					// unreachable;
					throw new IllegalStateException("Unsupported type! --" + type.getName());
			}
		}

		@SuppressWarnings("unchecked")
		@Override
		public <T extends Cryptogram> T decode(String algorithm, byte[] rawCryptoBytes, Class<T> type) {
			if (!isValidType(type)) {
				throw new IllegalStateException("Unsupported type! --" + type.getName());
			}
			CryptoServiceBase<?> cryptoService = innerGetCryptoService(algorithm);
			Collection<CryptobytesCodec<?>> cryptobytesCodecs = cryptoService.getProvidedService()
					.getCryptogramCodecs();

			CryptogramType cryptogramType = asCryptogramType(type);
			CryptobytesCodec<?> cryptobytesCodec = null;
			for (CryptobytesCodec<?> codec : cryptobytesCodecs) {
				if (codec.getCryptogramType() == cryptogramType) {
					cryptobytesCodec = codec;
				}
			}
			if (cryptobytesCodec == null) {
				throw new IllegalStateException("No CryptobytesCodec of algorithm[" + algorithm
						+ "] is supported the specified cryptogram type[" + cryptogramType.toString() + "]!");
			}

			Cryptobytes cryptobytes = cryptobytesCodec.encode(rawCryptoBytes);

			Cryptogram cryptogram = CRYPTOGRAM_ENCODING.encode(cryptoService.getAlgorithm(), cryptobytes);
			return (T) cryptogram;
		}

		@Override
		public Cryptogram decode(String cryptoText) {
			return decode(Base58Encoding.decode(cryptoText));
		}

		@Override
		public <T extends Cryptogram> T decode(String cryptoText, Class<T> type) {
			return decode(Base58Encoding.decode(cryptoText), type);
		}

		@Override
		public byte[] decodeToBytes(String cryptoText) {
			return Base58Encoding.decode(cryptoText);
		}

		private boolean isValidType(Class<?> cryptogramType) {
			if (cryptogramType == null) {
				return false;
			}
			if (cryptogramType == HashCode.class //
					|| cryptogramType == Signature.class //
					|| cryptogramType == PrivateKey.class //
					|| cryptogramType == PublicKey.class //
					|| cryptogramType == SymmetricKey.class) {

				return true;
			}
			return false;
		}

		private CryptogramType asCryptogramType(Class<?> cryptogramType) {
			if (cryptogramType == null) {
				throw new IllegalArgumentException("Null cryptogramType!");
			}
			if (cryptogramType == HashCode.class) {
				return CryptogramType.HASH_DIGEST;
			} else if (cryptogramType == Signature.class) {
				return CryptogramType.SIGNATURE;
			} else if (cryptogramType == PublicKey.class) {
				return CryptogramType.PUBLIC_KEY;
			} else if (cryptogramType == PrivateKey.class) {
				return CryptogramType.PRIVATE_KEY;
			} else if (cryptogramType == SymmetricKey.class) {
				return CryptogramType.SYMMETRIC_KEY;
			}

			throw new IllegalStateException("Unsupported type! --" + cryptogramType.getName());
		}

		private Cryptobytes decodeCryptobytes(Algorithm algorithm, byte[] cryptoBytes) throws CryptoException {
			checkAlgorithmEmpty();

			CryptoServiceBase<?> cryptoService = this.CONTEXT.cryptoServices.get(algorithm.getName());
			Collection<CryptobytesCodec<?>> cryptoBytesCodecs = cryptoService.getProvidedService()
					.getCryptogramCodecs();

			for (CryptobytesCodec<?> codec : cryptoBytesCodecs) {
				Cryptobytes cbytes = ENCODING.decodedContent(cryptoBytes, 0, cryptoBytes.length, codec);
				if (cbytes != null) {
					return cbytes;
				}
			}
			throw new CryptoException(
					"The specified crypto bytes are not supported by the provider of specified algorithm[" + algorithm
							+ "]!");
		}

		private CryptogramType decodeCryptogramType(Algorithm algorithm, byte[] cryptoBytes) {
			CryptoServiceBase<?> cryptoService = this.CONTEXT.cryptoServices.get(algorithm.getName());
			Collection<CryptobytesCodec<?>> cryptoBytesCodecs = cryptoService.getProvidedService()
					.getCryptogramCodecs();
			for (CryptobytesCodec<?> codec : cryptoBytesCodecs) {
				if (ENCODING.isSupported(cryptoBytes, 0, cryptoBytes.length, codec)) {
					return codec.getCryptogramType();
				}
			}
			return null;
		}

		@Override
		public byte[] encode(HashCode hashCode) {
			Cryptobytes cbytes = CRYPTOGRAM_ENCODING.decode(hashCode);
			return ENCODING.encode(hashCode.getAlgorithm(), cbytes);
		}

		@Override
		public byte[] encode(Signature signature) {
			Cryptobytes cbytes = CRYPTOGRAM_ENCODING.decode(signature);
			return ENCODING.encode(signature.getAlgorithm(), cbytes);
		}

		@Override
		public byte[] encode(PrivateKey privateKey) {
			Cryptobytes cbytes = CRYPTOGRAM_ENCODING.decode(privateKey);
			return ENCODING.encode(privateKey.getAlgorithm(), cbytes);
		}

		@Override
		public byte[] encode(PublicKey publicKey) {
			Cryptobytes cbytes = CRYPTOGRAM_ENCODING.decode(publicKey);
			return ENCODING.encode(publicKey.getAlgorithm(), cbytes);
		}

		@Override
		public byte[] encode(SymmetricKey symmetricKey) {
			Cryptobytes cbytes = CRYPTOGRAM_ENCODING.decode(symmetricKey);
			return ENCODING.encode(symmetricKey.getAlgorithm(), cbytes);
		}
	}

}
